/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// OpenGL ES 2.0 code

#include <jni.h>
#include <android/log.h>

#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define  LOG_TAG    "libgl2jni"
#define  LOGI(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  LOGE(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
GLuint gProgram;
GLuint gvPositionHandle;
GLuint gvTexCoordHandle;
GLuint gvSamplerHandle;
GLuint textureID;
static void printGLString(const char *name, GLenum s) {
    const char *v = (const char *) glGetString(s);
    LOGI("GL %s = %s\n", name, v);
}

static void checkGlError(const char* op) {
    for (GLint error = glGetError(); error; error
            = glGetError()) {
        LOGI("after %s() glError (0x%x)\n", op, error);
    }
}

static const char gVertexShader[] = 
 "attribute vec4 vPosition;\n"
 "attribute vec2 a_texCoord;\n"
 "varying vec2 v_texCoord;\n"
    "void main() {\n"
    "  gl_Position = vPosition;\n"
 "  v_texCoord = a_texCoord;\n"
    "}\n";

static const char gFragmentShader[] = 
    "precision mediump float;\n"
  "varyingvec2 v_texCoord;\n"
  "uniform sampler2D u_samplerTexture;\n"
  "void main() {\n"
  "  gl_FragColor = texture2D(u_samplerTexture,v_texCoord);\n"
  "}\n";
GLubyte testpixels[4 * 3] = { 255, 0, 0, // Red
                0, 255, 0, // Green
                0, 0, 255, // Blue
                255, 255, 0 // Yellow
                };
GLuint createSimpleTexture2D(GLuint _textureid, GLubyte* pixels,
                int width, int height, int channels) {

        // Bind the texture
        glActiveTexture(GL_TEXTURE0);
        checkGlError("glActiveTexture");
        // Bind the texture object
        glBindTexture(GL_TEXTURE_2D, _textureid);
        checkGlError("glBindTexture");

        GLenum format;
        switch (channels) {
        case 3:
                format = GL_RGB;
                break;
        case 1:
                format = GL_LUMINANCE;
                break;
        case 4:
                format = GL_RGBA;
                break;
        }
        // Load the texture
        glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format,
                        GL_UNSIGNED_BYTE, pixels);

        checkGlError("glTexImage2D");
        // Set the filtering mode
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );

        return _textureid;

}


GLuint loadShader(GLenum shaderType, const char* pSource) {
    GLuint shader = glCreateShader(shaderType);
    if (shader) {
        glShaderSource(shader, 1, &pSource, NULL);
        glCompileShader(shader);
        GLint compiled = 0;
        glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled);
        if (!compiled) {
            GLint infoLen = 0;
            glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen);
            if (infoLen) {
                char* buf = (char*) malloc(infoLen);
                if (buf) {
                    glGetShaderInfoLog(shader, infoLen, NULL, buf);
                    LOGE("Could not compile shader %d:\n%s\n",
                            shaderType, buf);
                    free(buf);
                }
                glDeleteShader(shader);
                shader = 0;
            }
        }
    }
    return shader;
}

GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) {
    GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource);
    if (!vertexShader) {
        return 0;
    }

    GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource);
    if (!pixelShader) {
        return 0;
    }

    GLuint program = glCreateProgram();
    if (program) {
        glAttachShader(program, vertexShader);
        checkGlError("glAttachShader");
        glAttachShader(program, pixelShader);
        checkGlError("glAttachShader");
        glLinkProgram(program);
        GLint linkStatus = GL_FALSE;
        glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
        if (linkStatus != GL_TRUE) {
            GLint bufLength = 0;
            glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength);
            if (bufLength) {
                char* buf = (char*) malloc(bufLength);
                if (buf) {
                    glGetProgramInfoLog(program, bufLength, NULL, buf);
                    LOGE("Could not link program:\n%s\n", buf);
                    free(buf);
                }
            }
            glDeleteProgram(program);
            program = 0;
        }
    }
    return program;
}
bool setupGraphics(int w, int h) {
    printGLString("Version", GL_VERSION);
    printGLString("Vendor", GL_VENDOR);
    printGLString("Renderer", GL_RENDERER);
    printGLString("Extensions", GL_EXTENSIONS);

    LOGI("setupGraphics(%d, %d)", w, h);
    gProgram = createProgram(gVertexShader, gFragmentShader);
    if (!gProgram) {
        LOGE("Could not create program.");
        return false;
    }
    gvPositionHandle = glGetAttribLocation(gProgram, "a_position");
    gvTexCoordHandle = glGetAttribLocation(gProgram, "a_texCoord");

    gvSamplerHandle = glGetAttribLocation(gProgram, "s_texture");

    // Use tightly packed data
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

    // Generate a texture object
    glGenTextures(1, &textureID);
    textureID = createSimpleTexture2D(textureID, testpixels, 2, 2, 3);
    checkGlError("glGetAttribLocation");
    LOGI("glGetAttribLocation(\"vPosition\") = %d\n",gvPositionHandle);
    glViewport(0, 0, w, h);
    checkGlError("glViewport");
    return true;
}

const GLfloat gTriangleVertices[] = { 0.0f, 0.5f, -0.5f, -0.5f,
        0.5f, -0.5f };
void renderFrame() {
 GLfloat vVertices[] = { -0.75f, 1.0f, 0.0f, // Position 0
                         0.0f, 0.0f, // TexCoord 0
                         -.75f, -1.0f, 0.0f, // Position 1
                         0.0f, 1.0f, // TexCoord 1
                         .75f, -1.0f, 0.0f, // Position 2
                         1.0f, 1.0f, // TexCoord 2
                         .75f, 1.0f, 0.0f, // Position 3
                         1.0f, 0.0f // TexCoord 3
                         };
 GLushort indices[] = { 0, 1, 2, 0, 2, 3 };

 GLsizei stride=5*sizeof(GLfloat);
    glClearColor(0.5f, 0.5f, 0.5f, 0.5f);
    checkGlError("glClearColor");
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    checkGlError("glClear");
    glEnable(GL_TEXTURE_2D);
    glUseProgram(gProgram);
    checkGlError("glUseProgram");
   // Load the vertex position
    glVertexAttribPointer(gvPositionHandle, 3, GL_FLOAT, GL_FALSE, stride,
      vVertices);
    // Load the texture coordinate
    glVertexAttribPointer(gvTexCoordHandle, 2, GL_FLOAT, GL_FALSE, stride,
      &vVertices[3]);
    glEnableVertexAttribArray(gvPositionHandle);
    glEnableVertexAttribArray(gvTexCoordHandle);
    // Bind the texture
    glActiveTexture(GL_TEXTURE0);

    glBindTexture(GL_TEXTURE_2D, textureID);
    // Set the sampler texture unit to 0
    glUniform1i(gvSamplerHandle, 0);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices);

 
/*
    glVertexAttribPointer(gvPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, gTriangleVertices);
    checkGlError("glVertexAttribPointer");
    glEnableVertexAttribArray(gvPositionHandle);
    checkGlError("glEnableVertexAttribArray");
    glDrawArrays(GL_TRIANGLES, 0, 3);
    checkGlError("glDrawArrays");
*/
}
